#include "Coin3dLearn.h"


// The control points for this surface
const float
pts[16][3] = {
  {-4.5, -2.0,  8.0},
  {-2.0,  1.0,  8.0},
  { 2.0, -3.0,  6.0},
  { 5.0, -1.0,  8.0},
  {-3.0,  3.0,  4.0},
  { 0.0, -1.0,  4.0},
  { 1.0, -1.0,  4.0},
  { 3.0,  2.0,  4.0},
  {-5.0, -2.0, -2.0},
  {-2.0, -4.0, -2.0},
  { 2.0, -1.0, -2.0},
  { 5.0,  0.0, -2.0},
  {-4.5,  2.0, -6.0},
  {-2.0, -4.0, -5.0},
  { 2.0,  3.0, -5.0},
  { 4.5, -2.0, -6.0}
};

// The knot vector
float
knots[8] =
{
  0, 0, 0, 0,
  1, 1, 1, 1
};

void Coin3dLearn::test0(QWidget* myWindow)
{
	SoSeparator* root = new SoSeparator();
	root->ref();

	//SoCube* cube = new SoCube();
	//root->addChild(cube);
	SoCone* cone = new SoCone;
	root->addChild(cone);

	SoQtExaminerViewer* eviwer = new SoQtExaminerViewer(myWindow);
	eviwer->setSceneGraph(root);
	eviwer->show();
	SoQt::show(myWindow);
	SoQt::mainLoop();

	root->unref();
	delete eviwer;
}

void Coin3dLearn::test1(QWidget* myWindow)
{
	//Make a scene containing a red cone
	SoSeparator* root = new SoSeparator;
	SoPerspectiveCamera* myCamera = new SoPerspectiveCamera;
	SoMaterial* myMaterial = new SoMaterial;
	root->ref();
	root->addChild(myCamera);
	root->addChild(new SoDirectionalLight);
	myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);

	//This transformation is modified to rotate the cone
	//This action should be set before the object to see
	SoRotationXYZ* myRotXYZ = new SoRotationXYZ;
	root->addChild(myRotXYZ);


	root->addChild(myMaterial);
	root->addChild(new SoCone);


	//An engine rotates the object, The output of myCounter
	//is the time in seconds since the program started
	//Connect this output to the angle field of myRotXYZ
	myRotXYZ->axis = SoRotationXYZ::Y; //rotate about X axis
	SoElapsedTime* myCounter = new SoElapsedTime;
	myRotXYZ->angle.connectFrom(&myCounter->timeOut);

	//Create a renderArea in which to see our scene graph
	//The render area will appear within the main window
	SoQtRenderArea* myRenderArea = new SoQtRenderArea(myWindow);

	//Make myCamera see everything
	myCamera->viewAll(root, myRenderArea->getViewportRegion());

	//Put out scene in myRenderArea, change the title
	myRenderArea->setSceneGraph(root);
	myRenderArea->setTitle("Engine Spin");
	myRenderArea->show();

	SoQt::show(myWindow);  //Display main window
	SoQt::mainLoop();  //Main Inventor event loop

	root->unref();
}

void Coin3dLearn::test2(QWidget* myWindow)
{
	//Make a scene containing a red cone
	SoSeparator* root = new SoSeparator;
	SoPerspectiveCamera* myCamera = new SoPerspectiveCamera;
	SoMaterial* myMaterial = new SoMaterial;
	root->ref();
	root->addChild(myCamera);
	root->addChild(new SoDirectionalLight);
	root->addChild(new SoTrackballManip);
	myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);
	root->addChild(myMaterial);
	root->addChild(new SoCone);

	//Create a renderArea in which to see our scene graph
	//The render area will appear within the main window
	SoQtRenderArea* myRenderArea = new SoQtRenderArea(myWindow);

	//Make myCamera see everything
	myCamera->viewAll(root, myRenderArea->getViewportRegion());

	//Put out scene in myRenderArea, change the title
	myRenderArea->setSceneGraph(root);
	myRenderArea->setTitle("Trackball");
	myRenderArea->show();

	SoQt::show(myWindow);  //Display main window
	SoQt::mainLoop();  //Main Inventor event loop

	root->unref();
}

void Coin3dLearn::test3(QWidget* myWindow)
{
	//Make a scene containing a red cone
	SoSeparator* root = new SoSeparator;
	root->ref();
	//SoMaterial* myMaterial = new SoMaterial;
	//myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);
	SoMaterial* bronze = new SoMaterial;
	// set field values
	bronze->ambientColor.setValue(.33, .22, .27);
	bronze->diffuseColor.setValue(.78, .57, .11);
	bronze->specularColor.setValue(.99, .94, .81);
	bronze->shininess = .28;
	root->addChild(bronze);
	root->addChild(new SoCone);

	//set up viewer
	SoQtExaminerViewer* myViewer = new SoQtExaminerViewer(myWindow);
	myViewer->setSceneGraph(root);
	myViewer->setTitle("Examiner Viewer");
	myViewer->show();

	SoQt::show(myWindow);  //Display main window
	SoQt::mainLoop();  //Main Inventor event loop
	root->unref();
}

void Coin3dLearn::test4(QWidget* myWindow)
{
	//Make a scene containing a red cone
	//SoSeparator* root = new SoSeparator;
	//root->ref();

	SoGroup* waterMolecule = new SoGroup;

	SoGroup* oxygen = new SoGroup;
	SoMaterial* redPlastic = new SoMaterial;
	SoSphere* sphere1 = new SoSphere;

	SoGroup* hydrogen1 = new SoGroup;
	SoGroup* hydrogen2 = new SoGroup;
	SoTransform* hydrogenXform1 = new SoTransform;
	SoTransform* hydrogenXform2 = new SoTransform;
	SoMaterial* whitePlastic = new SoMaterial;
	SoSphere* sphere2 = new SoSphere;
	SoSphere* sphere3 = new SoSphere;

	//set all field values for the oxygen atom
	redPlastic->ambientColor.setValue(1.0, 0.0, 0.0);
	redPlastic->diffuseColor.setValue(1.0, 0.0, 0.0);
	redPlastic->specularColor.setValue(0.5, 0.5, 0.5);
	redPlastic->shininess = 0.5;

	//set all field values for the hydrogen atoms
	hydrogenXform1->scaleFactor.setValue(0.75, 0.75, 0.75);
	hydrogenXform1->translation.setValue(0.0, -1.2, 0.0);
	hydrogenXform2->translation.setValue(1.1852, 1.3877, 0.0);
	whitePlastic->ambientColor.setValue(1.0, 1.0, 1.0);
	whitePlastic->diffuseColor.setValue(1.0, 1.0, 1.0);
	whitePlastic->specularColor.setValue(0.5, 0.5, 0.5);
	whitePlastic->shininess = 0.5;

	// Create a hierarchy
	waterMolecule->addChild(oxygen);
	waterMolecule->addChild(hydrogen1);
	waterMolecule->addChild(hydrogen2);
	oxygen->addChild(redPlastic);
	oxygen->addChild(sphere1);
	hydrogen1->addChild(hydrogenXform1);
	hydrogen1->addChild(whitePlastic);
	hydrogen1->addChild(sphere2);
	hydrogen2->addChild(hydrogenXform2);
	hydrogen2->addChild(sphere3);

	//root->addChild(waterMolecule);

	//set up viewer
	SoQtExaminerViewer* myViewer = new SoQtExaminerViewer(myWindow);
	myViewer->setSceneGraph(waterMolecule);
	myViewer->setTitle("Examiner Viewer");
	myViewer->show();

	SoQt::show(myWindow);  //Display main window
	SoQt::mainLoop();  //Main Inventor event loop
	waterMolecule->unref();
}

void Coin3dLearn::test5(QWidget* myWindow)
{
	////robot with legs
	SoSeparator* robot = new SoSeparator;

	//construct parts for legs (thigh, calf and foot)
	SoCube* thight = new SoCube;
	thight->width = 1.2;
	thight->height = 2.2;
	thight->depth = 1.1;

	SoTransform* calfTransform = new SoTransform;
	calfTransform->translation.setValue(0, -2.25, 0);

	SoCube* calf = new SoCube;
	calf->width = 1;
	calf->height = 2.2;
	calf->depth = 1;

	SoTransform* footTransform = new SoTransform;
	footTransform->translation.setValue(0, -2, 0.5);

	SoCube* foot = new SoCube;
	foot->width = 0.8;
	foot->height = 0.8;
	foot->depth = 2;

	//put leg parts together
	SoGroup* leg = new SoGroup;
	leg->addChild(thight);
	leg->addChild(calfTransform);
	leg->addChild(calf);
	leg->addChild(footTransform);
	leg->addChild(foot);

	SoTransform* leftTransform = new SoTransform;
	leftTransform->translation = SbVec3f(1, -4.25, 0);

	//left leg
	SoSeparator* leftLeg = new SoSeparator;
	leftLeg->addChild(leftTransform);
	leftLeg->addChild(leg);

	SoTransform* rightTransform = new SoTransform;
	rightTransform->translation.setValue(-1, -4.25, 0);

	//right leg
	SoSeparator* rightLeg = new SoSeparator;
	rightLeg->addChild(rightTransform);
	rightLeg->addChild(leg);

	//create body parts
	SoTransform* xf1 = new SoTransform;
	xf1->translation.setValue(0.0, 3.0, 0.0);

	SoMaterial* bronze = new SoMaterial;
	bronze->ambientColor.setValue(.33, .22, .27);
	bronze->diffuseColor.setValue(.78, .57, .11);
	bronze->specularColor.setValue(.99, .94, .81);
	bronze->shininess = .28;

	SoCylinder* myCylinder = new SoCylinder;
	myCylinder->radius = 2.5;
	myCylinder->height = 6;

	//construct body out of parts
	SoSeparator* body = new SoSeparator;
	body->addChild(xf1);
	body->addChild(bronze);
	body->addChild(myCylinder);
	body->addChild(leftLeg);
	body->addChild(rightLeg);

	//create head parts
	SoTransform* xf2 = new SoTransform;
	xf2->translation.setValue(0.0, 7.5, 0);
	xf2->scaleFactor.setValue(1.5, 1.5, 1.5);

	SoMaterial* silver = new SoMaterial;
	silver->ambientColor.setValue(.2, .2, .2);
	silver->diffuseColor.setValue(.6, .6, .6);
	silver->specularColor.setValue(.5, .5, .5);
	silver->shininess = .5;

	SoSphere* mySphere = new SoSphere;

	//construct head out of parts
	SoSeparator* head = new SoSeparator;
	head->addChild(xf2);
	head->addChild(silver);
	head->addChild(mySphere);

	robot->addChild(body);
	robot->addChild(head);

	//set up viewer
	SoQtExaminerViewer* myViewer = new SoQtExaminerViewer(myWindow);
	myViewer->setSceneGraph(robot);
	myViewer->setTitle("Robot with leg");
	myViewer->show();

	SoQt::show(myWindow);  //Display main window
	SoQt::mainLoop();  //Main Inventor event loop
}

int Coin3dLearn::test6(QWidget* myWindow, string ivFile)
{
	SoSeparator* root = new SoSeparator;
	root->ref();
	// Create a blinker node and put it in the scene. A blinker
	// switches between its children at timed intervals.
	SoBlinker* myBlinker = new SoBlinker;
	root->addChild(myBlinker);

	// Create three cameras.Their positions will be set later.
	// This is because the viewAll method depends on the size
	// of the render area, which has not been created yet.
	SoOrthographicCamera* orthoViewAll = new SoOrthographicCamera;
	SoPerspectiveCamera* perspViewAll = new SoPerspectiveCamera;
	SoPerspectiveCamera* perspOffCenter = new SoPerspectiveCamera;
	myBlinker->addChild(orthoViewAll);
	myBlinker->addChild(perspViewAll);
	myBlinker->addChild(perspOffCenter);

	// Create a light
	root->addChild(new SoDirectionalLight);
	// Read the object from a file and add to the scene
	SoInput myInput;
	if (!myInput.openFile(ivFile.c_str())) {
		cout << "fail to open " << ivFile << endl;
		return 1;
	}
		
	SoSeparator* fileContents = SoDB::readAll(&myInput);

	if (fileContents == NULL)
		return 1;
	SoMaterial* myMaterial = new SoMaterial;
	myMaterial->diffuseColor.setValue(0.8, 0.23, 0.03);
	root->addChild(myMaterial);
	root->addChild(fileContents);
	SoQtRenderArea* myRenderArea = new SoQtRenderArea(myWindow);
	// Establish camera positions.
	// First do a viewAll() on all three cameras.
	// Then modify the position of the off-center camera.
	SbViewportRegion myRegion(myRenderArea->getSize());
	orthoViewAll->viewAll(root, myRegion);
	perspViewAll->viewAll(root, myRegion);
	perspOffCenter->viewAll(root, myRegion);
	SbVec3f initialPos;
	initialPos = perspOffCenter->position.getValue();
	float x, y, z;
	initialPos.getValue(x, y, z);
	perspOffCenter->position.setValue(x + x / 2., y + y / 2., z + z / 4.);
	myRenderArea->setSceneGraph(root);
	myRenderArea->setTitle("Cameras");
	myRenderArea->show();

	SoQt::show(myWindow);
	SoQt::mainLoop();

	return 0;
}

void Coin3dLearn::test7(QWidget* myWindow)
{
	SoSeparator* root = new SoSeparator;
	root->ref();
	// Add a directional light
	SoDirectionalLight* myDirLight = new SoDirectionalLight;
	myDirLight->direction.setValue(0, -1, -1);
	myDirLight->color.setValue(1, 0, 0);
	root->addChild(myDirLight);
	// Put the shuttle and the light below a transform separator.
	// A transform separator pushes and pops the transformation
	// just like a separator node, but other aspects of the state
	// are not pushed and popped. So the shuttle's translation
	// will affect only the light. But the light will shine on
	// the rest of the scene.
	SoTransformSeparator* myTransformSeparator =
		new SoTransformSeparator;
	root->addChild(myTransformSeparator);

	// A shuttle node translates back and forth between the two
	// fields translation0 and translation1.
	// This moves the light.
	SoShuttle* myShuttle = new SoShuttle;
	myTransformSeparator->addChild(myShuttle);
	myShuttle->translation0.setValue(-2, -1, 3);
	myShuttle->translation1.setValue(1, 2, -3);

	// Add the point light below the transformSeparator
	SoPointLight* myPointLight = new SoPointLight;
	myTransformSeparator->addChild(myPointLight);
	myPointLight->color.setValue(0, 1, 0);
	root->addChild(new SoCone);
	SoQtExaminerViewer* myViewer =
		new SoQtExaminerViewer(myWindow);
	myViewer->setSceneGraph(root);
	myViewer->setTitle("Lights");
	myViewer->setHeadlight(FALSE);
	myViewer->show();
	SoQt::show(myWindow);
	SoQt::mainLoop();
}


void Coin3dLearn::test8(QWidget* myWindow)
{
	SoVolumeRendering::init();
	SoSeparator* root = new SoSeparator;
	root->ref();
	SbVec3s dim = SbVec3s(64, 64, 64);
	uint8_t* voxeldata = generate8bitVoxelSet(dim);
	// Add SoVolumeData to scene graph
	SoVolumeData* volumedata = new SoVolumeData();
	volumedata->setVolumeData(dim, voxeldata, SoVolumeData::UNSIGNED_BYTE);
	root->addChild(volumedata);
	// Add TransferFunction (color map) to scene graph
	SoTransferFunction* transfunc = new SoTransferFunction();
	root->addChild(transfunc);
	// Add VolumeRender to scene graph
	SoVolumeRender* volrend = new SoVolumeRender();
	root->addChild(volrend);
	auto viewer = new SoQtExaminerViewer(myWindow);
	viewer->setBackgroundColor(SbColor(0.1f, 0.3f, 0.5f));
	viewer->setSceneGraph(root);
	viewer->show();
	SoQt::show(myWindow);
	SoQt::mainLoop();
	delete viewer;
	root->unref();
	delete[] voxeldata;
	
}

SoSeparator* Coin3dLearn::makeSurface()
{
	SoSeparator* surfSep = new SoSeparator();
	surfSep->ref();

	// Define the Bezier surface including the control
	// points and a complexity.
	SoComplexity* complexity = new SoComplexity;
	SoCoordinate3* controlPts = new SoCoordinate3;
	SoNurbsSurface* surface = new SoNurbsSurface;
	complexity->value = 0.7f;
	controlPts->point.setValues(0, 16, pts);
	surface->numUControlPoints = 4;
	surface->numVControlPoints = 4;
	surface->uKnotVector.setValues(0, 8, knots);
	surface->vKnotVector.setValues(0, 8, knots);
	surfSep->addChild(complexity);
	surfSep->addChild(controlPts);
	surfSep->addChild(surface);

	surfSep->unrefNoDelete();
	return surfSep;
}

void Coin3dLearn::test9_BezSurf(QWidget* myWindow)
{
	SoSeparator* root = new SoSeparator;
	root->ref();

	// Create the scene graph for the carpet;
	SoSeparator* carpet = new SoSeparator;
	SoSeparator* surf = makeSurface();
	SoTexture2* tex = new SoTexture2;

	tex->ref();
	tex->filename.setValue("./data/diamondRug.rgb");
	carpet->addChild(tex);
	carpet->addChild(surf);
	root->addChild(carpet);

	SoQtExaminerViewer* eviwer = new SoQtExaminerViewer(myWindow);
	eviwer->setSceneGraph(root);
	eviwer->show();
	SoQt::show(myWindow);
	SoQt::mainLoop();

	root->unref();
	delete eviwer;
}

uint8_t* Coin3dLearn::generate8bitVoxelSet(SbVec3s& dim)
{
	const size_t blocksize = dim[0] * dim[1] * dim[2];
	uint8_t* voxels = new uint8_t[blocksize];
	(void)memset(voxels, 0, blocksize);
	float t = 0;
	while (t < 50) {
		SbVec3f v(sin((t + 1.4234) * 1.9) * sin(t) * 0.45 + 0.5,
			cos((t * 2.5) - 10) * 0.45 + 0.5,
			cos((t - 0.23123) * 3) * sin(t + 0.5) * cos(t) * 0.45 + 0.5);
		assert(v[0] < 1.0f && v[1] < 1.0f && v[2] < 1.0f);
		const int nx = int(dim[0] * v[0]);
		const int ny = int(dim[1] * v[1]);
		const int nz = int(dim[2] * v[2]);
		const int memposition = nz * dim[0] * dim[1] + ny * dim[0] + nx;
		voxels[memposition] = (uint8_t)(255.0 * cos(t));
		t += 0.001;
	}
	return voxels;
}
